Dlaczego bazy danych są nieefektywne?

0
ajp napisał(a):

Widzę, że jest zajadła dyskusja, więc próbowałem się dowiedzieć z Jak postgresql przechowuje tabele, o czym, ale może za wcześnie odpadłem. Wygląda, że porównujesz zapytanie SQL z programem w C++, który jednak robi coś prostszego, bo nie ma sortowania.

Z tego co pamiętam, pisałem o tym sortowaniu. Ale wtedy już byłem zmęczony, może jednak nie napisałem, a może napisałem niezrozumiale, a może Tobie tamta uwaga wydała się nie istotan i nie wczytałeś się w tekst. A może nawet gdzieś się rypłem w tych oszacowaniach i nie jest to aż 100tys. Na pewno kompleksowy program w C++, czyli z sortowaniem, z limit i z indeksami, ułatwiłby rozmowę. Nie obiecuję, ale postaram się zrobić i założymy nowy wątek. Pewnie i tak mi odpowiedzą że jestem debilem, ignorantem, a program jest z d**y, ale na to chyba w ogóle nie ma rady ;-)

ajp napisał(a):

Chyba można przyjąć, że najlepszy czas wykonania SQL jest wtedy, gdy dane rzeczywiście są zbuforowane w RAM, a wtedy jest tylko o rząd wielkości wolniej niż w przypadku programu w C++. Poza tym nie wiadomo, jakie są rozkłady wartości w tabeli i czy są policzone statystyki. Od tego może mocno zależeć, czy użycie indeksów jest sensowne.

Zgadzam się, że można eksperyment jeszcze dokładniej przeprowadzić. Po statystykach procesu można dodatkowo upewnić się, że baza danych naprawdę nie korzysta z IO, pliku swap, itd. W sumie zwykły zapis logów w pliku spowalnia odpowiedź serwera, można trochę przeoczyć.

ajp napisał(a):

Czy coś pominąłem?

Myślę że to co najważniejsze już ja wtedy uwzględniłem. Ale masz rację, że można postarać się jeszcze bardziej i upewnić się można na dodatkowe sposoby czy baza na pewno nie korzysta z dysku twardego.

0
artur_bredzki napisał(a):

Na pewno kompleksowy program w C++, czyli z sortowaniem, z limit i z indeksami, ułatwiłby rozmowę. Nie obiecuję, ale postaram się zrobić i założymy nowy wątek.
No początek prościej będzie zrobić SQL bez sortowania i z SUM(), nawet jeśli to nie jest to, co docelowo chcesz. Może też z usuniętymi indeksami, żeby było porównywalnie (tzn. też full scan). To już będzie jakaś informacja, czy z tą bazą jest aż tak tragicznie.

0

@op:

Nie chciałbym zabrzmieć jak jakiś hejter czy coś.
Natomiast przypomniało mi się, jak miałem zajęcia z pewnym gościem, który należał do teamu administrującego db w banku. W takim średnim banku jak na polskie warunki, w ciągu 12-13 dni było robionych ponad miliard operacji na db, już nie mówiąc o tym, że ilość "insertów" w tym czasie przyrastała od 0 do 1TB. Baza była postawiona na Oraclu.
Po co ta historyjka? Ano po to, żeby ciut uzmysłowić, że rozwiązania SQLowe dostępne na rynku są naprawdę dobre, i wg. mnie to próbujesz wyważyć otwarte drzwi.
No ale próbować nikt Tobie nie zabroni, wierze, że masz dużego skilla w C/C++ i sobie poradzisz. Tylko, żeby na końcu się nie okazało, że Twój kod będzie podobny do tego co jest zaimplementowane w SQL :)

0
Duży Terrorysta napisał(a):

@op:
Nie chciałbym zabrzmieć jak jakiś hejter czy coś.

Brzmi ok.

Duży Terrorysta napisał(a):

Natomiast przypomniało mi się, jak miałem zajęcia z pewnym gościem, który należał do teamu administrującego db w banku. W takim średnim banku jak na polskie warunki, w ciągu 12-13 dni było robionych ponad miliard operacji na db, już nie mówiąc o tym, że ilość "insertów" w tym czasie przyrastała od 0 do 1TB. Baza była postawiona na Oraclu.

To teraz ja nie chcę zabrzmieć niewdzięcznie, bo generalnie infromacje które przekazałeś są interesujące. Niemniej brakuje z mojego punktu widzenia dwóch ważnych szczegółów. Pierwszy szczegół dotyczy tego, na jakim sprzęcie, na ilu komputerach był zainstalowany system. Drugi szczegół dotyczy tego, czy system zrównoleglał operacje w sposób inny niż oferuje sama baza danych. Proszę, nie zrozum mnie źle, nawet kiepska baza może umożliwić 1TB insertów w godzinę, jeśli trud zrównoleglenia przeniesie się na projektanta i programistów systemu, a nie na bazę danych. Nie chcę zasugerować że Oracle jest kiepską bazą, tylko po prostu brakuje mi tych szczegółów.

Kiedyś na bazie postgresql napisałem system, który w jednym requescie http wykonuje milion requestów do serwera postgres. Co, niemożliwe?
No właśnie, niemożliwe, bo na postgresa była nakładka do buforowania danych i defacto request do bazy leciał statystycznie raz na milion razy.

Po co ta historyjka? Ano po to, żeby ciut uzmysłowić, że rozwiązania SQLowe dostępne na rynku są naprawdę dobre, i wg. mnie to próbujesz wyważyć otwarte drzwi.

No ale próbować nikt Tobie nie zabroni, wierze, że masz dużego skilla w C/C++ i sobie poradzisz. Tylko, żeby na końcu się nie okazało, że Twój kod będzie podobny do tego co jest zaimplementowane w SQL :)

Nie mam jakiegoś nadzwyczajnego skilla w C/C++, przeciętnie kodzę. Właściwie to ten wątek założyłem z tego powodu, przed którym mnie ostrzegasz. Zastanawiam się co takiego jeszcze bazy robią, czego przeciętny programista Java lub C++ nie da rady zrobić w rozsądnym czasie.

4

Zastanawiam się co takiego jeszcze bazy robią, czego przeciętny programista Java lub C++ nie da rady zrobić w rozsądnym czasie.

Guzik prawda. Gdybyś się zastanawiał to przeczytałbyś to co ci poleciłem wcześniej:
http://people.inf.elte.hu/nikovits/DB2/Ullman_The_Complete_Book.pdf

To by odpowiedziało na wszystkie twoje pytania. Ale ty wolisz snuć jakieś dywagacje. Masz w tej ksiażce (gdzieś od połowy) opisane dokładnie w jaki sposób zaimplementowane są systemy bazodanowe, jak i dlaczego składują dane, jak działa parsowanie zapytań, optymalizacja kosztowa, jakie algorytmy są stosowane np. do złączania czy sortowania, w jaki sposób baza obsługuje wielodostęp, jak implementuje się izolacje transakcji, słowem: wszystko.

1

Owe 100tys. wydało mi się tak naciągane, że zrobiłem szybki test ;)

Żeby nie komplikować testu troszeczkę uprościłem program autora wątku:

#include <cstdio>
#include <cstdlib>
#include <ctime>
 
 
#define SIZE (20 * 1000000)
 
 
struct IF3 {
    int id;
    int value;
    int pole1;
    int pole2;
    int pole3;
};
 
static void init( IF3 if3[] ) {
    for( int i=0 ; i<SIZE ; i++ ) {
        if3[i].id = i+1;
        if3[i].value = rand() % 1024;
        if3[i].pole1 = rand() % 32;
        if3[i].pole2 = rand() % 32;
        if3[i].pole3 = rand() % 1024;
    }
}
 
static int test1( const IF3 if3[] , const int v1 , const int v2, const int v3) {
    int sum = 0;
    for( int i=0 ; i<SIZE ; i++ ) {
        if(if3[i].pole1 == v1 && if3[i].pole2 == v2 && if3[i].pole3 == v3)
            sum += if3[i].value;
    }
    return sum;
}
 
 
int main(int argc, char *argv[])
{
    IF3 *const if3 = new IF3[SIZE];
    srand(time(NULL));
    init( if3 );
    int sum = 0;
    for( int i=0 ; i<1000 ; i++ )
        sum += test1( if3, (i+5) % 32, (i*5+4) % 32, (i*13) % 1024);
    delete[] if3;
    printf("sum = %d\n",sum);
    fflush(stdout);
    return 0;
}

Stworzyłem w mysql tabelę do testów:

CREATE TABLE `bench1` (
 `val` int(11) NOT NULL,
 `f1` int(11) NOT NULL,
 `f2` int(11) NOT NULL,
 `f3` int(11) NOT NULL
);

Wypełniłem ją dodając 20mln rekordów:

public static final int SIZE = 20 * 1000000;
...
PreparedStatement st = connection.prepareStatement("insert into bench1 (val, f1, f2, f3) values (?,?,?,?)");
for (int i = 0; i < SIZE; i++) {
  st.setInt(1, (int)(Math.random() * 1024));
  st.setInt(2, (int)(Math.random() * 32));
  st.setInt(3, (int)(Math.random() * 32));
  st.setInt(4, (int)(Math.random() * 1024));
  st.executeUpdate();
}

Przetestowałem wykonując 1000 zapytań i uwzględniając w where wartości wszystkich 3 kolumn:

PreparedStatement st = connection.prepareStatement("select sum(val) from bench1 where f1 = ? and f2 = ? and f3 = ?");
int sum = 0;
for( int i=0 ; i < 1000 ; i++ ) {
  st.setInt(1, (i+5) % 32); 
  st.setInt(2, (i*5+4) % 32);
  st.setInt(3, (i*13) % 1024);
  ResultSet rs = st.executeQuery();
  if (rs.next()) {
    sum += rs.getInt(1);
  }
  rs.close();
}
st.close();
System.out.println("sum = "+ sum);

Program w C wykonał się w około 25s.
Test z 1000 zapytań do bazy wykonał się w 934s. (łącznie z kompilacją projektu w javie ;))
Prosta matematyka mówi, że "rozmawiając" z bazą danych zadanie zostało wykonane około 37 razy wolniej.
37 to zdecydowanie lepiej niż 100tyś. :)

0

@sihox
A wrzuć tam porównywanie stringów zamiast intów to będzie lepsze porównanie. Wydaje mi się, że INT(11) będzie przechowywany w jakiejś dziwnej formie, zdecydowanie innej niż czysty int.

0
Shalom napisał(a):

Zastanawiam się co takiego jeszcze bazy robią, czego przeciętny programista Java lub C++ nie da rady zrobić w rozsądnym czasie.

Guzik prawda. Gdybyś się zastanawiał to przeczytałbyś to co ci poleciłem wcześniej:
http://people.inf.elte.hu/nikovits/DB2/Ullman_The_Complete_Book.pdf

Spokojnie, ja tę pozycję wpisałem na listę swoich lektur obowiązkowych. Co prawda piszesz, że można ją szybko przeczytać ze zrozumieniem, ale tego nie wziąłem na poważnie. Książki do algorytmów czytałem np. 3 miesiące, a choć niektóre rozdziały pominałem, to uważam że i tak czytałem w zbytnim pośpiechu. Kiedyś zabiorę się do tej lektury z nalezytym jej szacunkiem, a jeśli naprawdę można ją przerobić przez kilka dni, to na pewno ona nie uzupełni mojej wiedzy.

Shalom napisał(a):

To by odpowiedziało na wszystkie twoje pytania. Ale ty wolisz snuć jakieś dywagacje. Masz w tej ksiażce (gdzieś od połowy) opisane dokładnie w jaki sposób zaimplementowane są systemy bazodanowe, jak i dlaczego składują dane, jak działa parsowanie zapytań, optymalizacja kosztowa, jakie algorytmy są stosowane np. do złączania czy sortowania, w jaki sposób baza obsługuje wielodostęp, jak implementuje się izolacje transakcji, słowem: wszystko.

Hmmm to mnie zachęca do przerobienia tej lektury.

Swoją drogą, mam książkę 'postresql wysokowydajny'. Przeczytałem gdzieś do połowy. Autor na początku tej książki opisywał wady i zalety systemów plików. Myślę sobie, system plików to rzecz ważna dla baz danych, więc zacząłem się wczytywać i nawet można było znaleźć ciekawe informacje, np. pisał o tym że system plików zbędnie buforuje, bo baza ma swój bufor, albo że system zbędnie dziennikuje, poniważ baza ma swój dzienni. Jednak potem autor leciał po wersjach systemów (podręcznik lekko zamieniał się w historię systemów plików), zamiast opisać zalety i wady najlepszych wersji. Potem autor zaczął brnąć w takie szczegóły, jak 'algorytm windy' i jego parametry. Dzięki takim szczegółom można zyskać może kilka procent na wydajności, a strasznie trudno jest dobrać optymalne parametry dla statystycznego rozkładu zapytań jaki przewiduje się na przyszłość. Nie doczytałem książki do końca, książka choć zawiera ważne informacje, to niestety są one wplecione w dużą ilość zbędnych szczegółów lub banałów.

0
sihox napisał(a):

Owe 100tys. wydało mi się tak naciągane, że zrobiłem szybki test ;)
Żeby nie komplikować testu troszeczkę uprościłem program autora wątku:

Coś mi się zdaje, że uprościłeś właśnie o ten aspekt, do którego moja baza nie umie porządnie wykorzystać indeksów :) Ale cieszę się, jest jakiś kod na forum, jakis pomiar, więc i mnie to zachęca do dokładniejszych pomiarów. Mam tylko gorącą prośbę, aby nie obcinać warunku IN ( od kilku do kilkudziesięciu intów ) i aby test zrobić na bazie typu sqlite, bo ta baza z oczywistych względów może dać inne wyniki niż mysql lub postgres.

0

Żeby podtrzymać realność przykładu i praktyczną przydatność, dodałem do tabeli też trzy pola tekstowe. Na moim kompie długo generują się przykładowe dane, więc wyniki podam pewnie w następnym poście.

sihox napisał(a):

Owe 100tys. wydało mi się tak naciągane, że zrobiłem szybki test ;)
Żeby nie komplikować testu troszeczkę uprościłem program autora wątku:
[...]

PreparedStatement st = connection.prepareStatement("select sum(val) from bench1 where f1 = ? and f2 = ? and f3 = ?");

Teraz muszę sprostować kilka nieporozumień. Przykładowe oryginalne zapytanie wyglądało tak:

select * from table where f1=3 and f2=5 and f3 in ( kilkanaście do kilkudziesięciu intów ) and id <= 123456 order by id desc limit 10;

Program w C++ uprościłem do:

 select sum(val) from bench1 where f1 = 3 and f2 = 5 and f3 = IN ( ... ) 

ponieważ z czasu wykonania tak uproszczonego programu można coś wywnioskować o czasie wykonania programu realizującego oryginalne przykładowe zapytanie. Sposób wnioskowania opisalem poprzednio.

sihox napisał(a):

Program w C wykonał się w około 25s.

Czyli czas podobny jak u mnie, mój czas to około 41 sekund, ale program nie był uproszczony.

sihox napisał(a):

Test z 1000 zapytań do bazy wykonał się w 934s. (łącznie z kompilacją projektu w javie ;))
Prosta matematyka mówi, że "rozmawiając" z bazą danych zadanie zostało wykonane około 37 razy wolniej.
37 to zdecydowanie lepiej niż 100tyś. :)

Czyli bez indeksów jedno 'uproszczone' zapytanie wykonuje się u Ciebie około 0.9s.

Właśnie widzę, że zakończyło się u mnie wstawianie danych. Bałem się, że inne programy zaczną swapować, więc ograniczyłem ilość rekordów z początkowych 20ml do 10mln. Upewnilem się że baza danych jest dużo mniejsza niż pamięć przydzielona bazie, więc wszystkie dane powinny być w pamięci ram.

Mierzę czas pelnego zapytania ( z warunkiem f3 in ( 17 intów ) ) bez indeksów i wychodzi od 1400 do 1700ms, średnio 1450ms.
Prosta matematyka mówi 1450ms / 40ms = 36 razy wolniej.

Zakładam index na id. Baza poradziła sobie zaskakująco dobrze. Czas spadł do 50ms, czyli mniej więcej do takiego, jaki jest w C++ na
fullscan.

Dalej jeszcze wieksza niespodzianka! Zakładam indeks, który według mojego uznania powinien być optymalny do tego zapytania. Czas
zapytania 1ms!!! Nie mogę uwierzyć że czas jest taki krótki. Sprawdzam plan zapytania, widzę, że baza optymalnie wykorzystała założony indeks, zarówno do filtrowania, jak i do sortowania. Od początku mówiłem, że przy indeksach baza nie musi wykonać żadnej ciężkiej roboty przy sortowaniu.

Wracam do bazy oryginalnej. Upewniam się, czy jest analogiczny indeks jak w bazie testowej. Sprawdzam plan zapytania - jest zupełnie inny. Plan wygląda tak, jakby dla każdej wartości w nawiasie baza robiła nested loop. Czasy zapytania dla 4mln rekordów w bazie oryginalnej nigdy nie spadają poniżej 600ms, czasami trwają nawet 6s.

Teraz zgłupiałem kompletnie. To samo zapytanie na te same kolumny. Tak samo zdefiniowane indeksy i użyte w obu przypadkach. A plany zapytania i czasy zupełnie inne. Masakra jakaś...

Moje podsumowanie jest takie. Baza do tego zapytania czasami może optymalnie użyć indeksów, ale nie zawsze. Gdy nie użyje optymalnie, to różnica w czasie wykonania siega 100tys razy, gdy użyje optymalnie, to baza odpowiada w ciągu kilku microsekund (gdyby nie narzut na parsowanie sqla). Te kilka microsekund było wyliczonym przeze mnie czasem toretycznym, więc jakieś niezbędne narzuty rzędów wielkości przez bazę to mit. Baza do tego zapytania może optymalnie wykorzystać indeksy, najwięcej czasu trwa parsowanie sqla. Jeśli dane są w RAM, to w przypadku tego zapytania, baza powina precyzyjnie skoczyć do danych i je podać, bez żadnego filtrowania i najwyraźniej czasami to się bazom udaje.

Na bazie testowej zapytanie trwa 1ms z parsowaniem sqla i przesyłaniem rezutatów po TCP/IP. Na bazie oryginalnej trwa nawet 6s (dodam jeszcze, że w bazie oryginalnej jest mniej rekordów!). Nawet tak mierzona różnica wynosi 6tys razy.

Najciekawsze pytanie: dlaczego na oryginalnej bazie jest zupełnie inny plan zapytania???

W orgyginalnej bazie mam jeszcze kilka bardzo małych tabel. Oczywiście nie dokonuję żadnych złączeń z tymi tabelkami, zapytanie jest bez zmian. Mam założone inne indeksy na inne warianty zapytania, ale baza oryginalna wybiera ten sam indeks co baza testowa, inny jest tylko plan zapytania. Tabela w oryginalnej bazie ma więcej pól, ale są one tylko do prezentacji, nie nakładam na inne pola żadnych warunków. Baza oryginalna i testowa są założone na tym samym komputerze z tym samym plikiem konfiguracyjnym. Żadne statystyki nie mogły się wygenerować, bo obie bazy są zakładane z bardzo podobnego skryptu, obie bazy są świeże. WTF?

0

to co, bazujac na ostatnim doswiadczeniu przyznasz w koncu ze twoje problemy wynikaja z niewlasciwej i nieumiejetnej konfiguracji baz danych?
swoja droga male szanse zeby ktos ci wywrozyl co robisz zle. potrzebne by byly konkrety. taka drobna uwaga jeszcze - bazy danych sa dosc przewidywalne (no moze poza my/mssql ;)) wiec to nie jest tak ze sobie zakladasz ze magicznie cos bedzie jakos dzialalo bo tobie sie wydaje ze to ma sens, zamiast tego lepiej gdybys poznal rozwiazanie ktorego uzywasz a nie oczekiwal dzialania innego niz zaimplementowane. to tak jak bys napisal program w c++ wczytujacy inty a oczekiwal ze jak wpiszesz liczbe slownie to program sie polapie bo to przeciez oczywiste (dla ciebie).

artur_bredzki napisał(a):

Pewnie i tak mi odpowiedzą że jestem debilem, ignorantem, a program jest z d**y, ale na to chyba w ogóle nie ma rady
ktorys raz juz powtarzasz ze zostales okreslony debilem podczas gdy nikt tego nie zrobil. ignorantem owszem. na zasadzie analogii - wlasnie udowodnilam ze licze przynajmniej 100x szybciej niz komputer, dodalam w pamieci 2+2, bez zadnej rozgrzewki, a lapek ledwo zaczal sie bootowac. szach mat, nie przekonasz mnie ze komputery w ogole sa przydatne do obliczen.

Duży Terrorysta napisał(a):

Kiedyś na bazie postgresql napisałem system, który w jednym requescie http wykonuje milion requestów do serwera postgres. Co, niemożliwe?
No właśnie, niemożliwe, bo na postgresa była nakładka do buforowania danych i defacto request do bazy leciał statystycznie raz na milion razy.
i co to niby udowadnia albo postuluje? ze bulk operations to oszukiwanie?

Duży Terrorysta napisał(a):

Nastanawiam się co takiego jeszcze bazy robią, czego przeciętny programista Java lub C++ nie da rady zrobić w rozsądnym czasie.
wyjasnij mi w takim razie czemu oburzasz sie na nazywanie cie ignorantem :)

0
katelx napisał(a):

to co, bazujac na ostatnim doswiadczeniu przyznasz w koncu ze twoje problemy wynikaja z niewlasciwej i nieumiejetnej konfiguracji baz danych?

Wypraszam sobie takie uwagi koleżanko.

Po pierwsze to ja sam oszacowałem czas tego zapytania na microsekundy, ponieważ dobrze wiem jak działają b-drzewa i według tego co sobie wyobraziłem, bazy powinny do tego zapytania optymalnie użyć b-drzew. Niektórzy doszukiwali się tutaj jakiś rzędów wielkości które baza musi wykonać, inni myśleli że 20mln rekordów w tabeli może być wyzwaniem, a ja po prostu pytałem na co te rzędy wielkości baza zużywa, skoro program w C++ może to wykonać w ciągu microsekund. W tym przypadku dzięki b-drzewom baza nie powinna nic sortować, nic albo prawie nic filtrować, tylko precyzyjnie skoczyć do właściwych danych.

Po drugie indeksy założyłem właściwe, o czym świadczy wynik zgodny z oczekiwanym na bazie testowej.

W postresie dzieje się coś dziwnego. Zachowuje się tak jak przypuszczałem: na analogicznej bazie, na identycznym zapytaniu, na identycznym indeksie, buduje inny plan zapytania i ma narzut około 100tys razy względem optymalnego.

katelx napisał(a):

swoja droga male szanse zeby ktos ci wywrozyl co robisz zle. potrzebne by byly konkrety. taka drobna uwaga jeszcze - bazy danych sa dosc przewidywalne (no moze poza my/mssql ;)) wiec to nie jest tak ze sobie zakladasz ze magicznie cos bedzie jakos dzialalo bo tobie sie wydaje ze to ma sens, zamiast tego lepiej gdybys poznal rozwiazanie ktorego uzywasz a nie oczekiwal dzialania innego niz zaimplementowane.

Ale mi sie nic błędnego nie wydawało! Wiedziałem że taka struktura bazy danych powinna optymalnie współpracować z b-drzewami i miałem rację, bo na bazie testowej to działa. Postgres na bazie oryginalnej buduje inny plan zapytania, ja tylko nie wiem tego dorbiazgu, nie wiem czemu.

katelx napisał(a):

to tak jak bys napisal program w c++ wczytujacy inty a oczekiwal ze jak wpiszesz liczbe slownie to program sie polapie bo to przeciez oczywiste (dla ciebie).

Nie, to nie było tak, nigdy nie zastosowałem podobnego wnioskowania do żadnej bazy danych! Powtarzam, dobrze wiem czym są b-drzewa, dobrze dobrałem strukturę bazy do możliwości b-drzew i indeksy założyłem takie, aby postgres utworzył odpowiednie b-drzewa.

artur_bredzki napisał(a):

Pewnie i tak mi odpowiedzą że jestem debilem, ignorantem, a program jest z d**y, ale na to chyba w ogóle nie ma rady

ktorys raz juz powtarzasz ze zostales okreslony debilem podczas gdy nikt tego nie zrobil.

Nieprawda, zaszczytnym mianem debila też zostałem nazwany.

artur_bredzki napisał(a):

ignorantem owszem. na zasadzie analogii - wlasnie udowodnilam ze licze przynajmniej 100x szybciej niz komputer, dodalam w pamieci 2+2, bez zadnej rozgrzewki, a lapek ledwo zaczal sie bootowac. szach mat, nie przekonasz mnie ze komputery w ogole sa przydatne do obliczen.

Właśnie porównujesz kogos, kto optymalnie zaprojektował bazę, optymalnie dobrał indeksy, dokładnie oszacował czas zapytania i nie wie tylko jednego drobiazgu, do kogoś kto postępuje debilnie od początku do końca.

Duży Terrorysta napisał(a):

Kiedyś na bazie postgresql napisałem system, który w jednym requescie http wykonuje milion requestów do serwera postgres. Co, niemożliwe?
No właśnie, niemożliwe, bo na postgresa była nakładka do buforowania danych i defacto request do bazy leciał statystycznie raz na milion razy.
i co to niby udowadnia albo postuluje? ze bulk operations to oszukiwanie?

To zupełni nic nie miało udowadniać, wyrwałaś to z kontekstu. To był przykład uwypuklający pewne braki w informacji jakie przekazał mi rozmówca.

Duży Terrorysta napisał(a):

Nastanawiam się co takiego jeszcze bazy robią, czego przeciętny programista Java lub C++ nie da rady zrobić w rozsądnym czasie.

wyjasnij mi w takim razie czemu oburzasz sie na nazywanie cie ignorantem :)

Dlatego się oburzam, bo się właśnie okazało że bazy nic szczególnego nie robią, poza tym co robi tamten program. Przetworzenie 20mln rekordów w RAM to pikus dla baz, żadne sortowanie nie pochłania dużo czasu, żadne rzędy wielkości narzutów nie mają miejsca, nawet to że baza danych musi być przygotowana na działanie wielowątkowe i transakcyjne nie spowalnia jej szczególnie względem programu napisanego w C++. Właśnie ten mój program w przybliżeniu pokazuje jak bazy przetwarzają dane gdy są zbuforowane w RAM. Ten program nie był ani błędny, ani śmieszny, tylko był bardzo adekawtny do zadania. Ten program był potwierdzeniem moich przypuszczeń, jak baza danych powinna przetwarzać dane. A że przetwarzała znacznie wolniej, to zastanawiałem się czy czegoś jednak nie wiem, czy może postgres nieoptymalnie używa indeksów, chwilami sam wątpiłem czy dobrze te b-drzewa nadają się do tego zapytania. Ale nadają się, tylko postgres z jakiś nieznanych mi powodów, nie zawsze wykorzystuje je optymalnie.

0

W bazach danych fajny jest sam język zapytań SQL - jest to język 4-tej generacji tzn. opisujemy jedynie to co chcemy otrzymać za pomocą prostych relacji. W C, C++ programista musi napisać wszystko krok po korku.

0

Upewnilem się że baza danych jest dużo mniejsza niż pamięć przydzielona bazie, więc wszystkie dane powinny być w pamięci ram.

Dawno nie czytałem większej bzdury. Baza i tak musi sobie te dane odpowiednio wczytać z dysku. To nie jest tak że jak baza ma odpowiednio dużo ramu to sobie przy starcie ładuje wszystko do pamięci ;]

Plan zapytania może być inny zwyczajnie dlatego że optymalizator kosztowy uznał ze tak będzie lepiej. Nie jest on nieomylny, ale mógł uznać że jakaś skomplikowana część twojego zapytania szybciej wykona się w taki a nie inny sposób. Nie znając szczegółów ani różnic między tymi twoimi bazami trudno ocenić czemu.

0

autorze, to może pokaż im wszystkim kod który zrobiłeś, to będzie wyrocznia - albo ty sie zbłaźnisz albo każdy wypowiadający się w tym temacie

0

Autorze pokaż kod - popieram przedmówcę.

2

Proszę o przeniesienie tego wątku do "perełek". Zdecydowanie najlepszy w kategorii.

0
Shalom napisał(a):

Upewnilem się że baza danych jest dużo mniejsza niż pamięć przydzielona bazie, więc wszystkie dane powinny być w pamięci ram.

Dawno nie czytałem większej bzdury. Baza i tak musi sobie te dane odpowiednio wczytać z dysku. To nie jest tak że jak baza ma odpowiednio dużo ramu to sobie przy starcie ładuje wszystko do pamięci ;]

A gdzie ja pisałem że zaraz po starcie są dane od razu w pamięci ram? :) Ja tylko upewniłem się, że silnik nie korzysta z dysku w trakcie wykonywania zapytania :)

Shalom napisał(a):

Plan zapytania może być inny zwyczajnie dlatego że optymalizator kosztowy uznał ze tak będzie lepiej. Nie jest on nieomylny, ale mógł uznać że jakaś skomplikowana część twojego zapytania szybciej wykona się w taki a nie inny sposób. Nie znając szczegółów ani różnic między tymi twoimi bazami trudno ocenić czemu.

Też nie wiem czemu optymalizator popełnia tak fatalny błąd. Bazy są bardzo podobne. Testowa baza jest zrobiona przez usunięcie tego co zbędne dla tego zapytania z bazy oryginalnej. Indeksy i zapytania są identyczne w obu bazach. Pola wpływające na wyszukane rekordy też są identyczne. Bazy różnią się drobiazgami, np. są inne nazwy pól, dane są losowe, więc trochę inne są dane w obu bazach. Na 2 razy większej tabeli testowej zapytanie bez indeksów działa 4 razy szybciej niż na oryginalnej. Nie mam pojęcia co zrobić, żeby na oryginalnej też wykonywal zapytanie tak szybko. Jedno jest pewne, może wykonać to zapytanie bardzo szybko.

0

Jakikolwiek kod mogę wrzucić, tyle że ten testowy akurat działa bardzo wydajnie.

rollback;

\pset pager off
\timing off
SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

\c postgres;
set role postgres;


DROP DATABASE IF EXISTS testabc;
DROP TABLESPACE IF EXISTS testabcspace;
DROP ROLE IF EXISTS testabc;


CREATE ROLE testabc PASSWORD 'testabc' NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;
CREATE DATABASE testabc OWNER testabc ENCODING 'UTF-8';
\c testabc;


create or replace function random_string(length integer) returns text as 
$$
declare
  chars text[] := '{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,r,s,t,u,v,w,x,y,z,0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,R,S,T,U,V,W,X,Y,Z}';
  result text := '';
  i integer := 0;
begin
  for i in 1..length loop
    result := result || chars[1+random()*(array_length(chars, 1)-1)];
  end loop;
  return result;
end;
$$ language plpgsql;

CREATE SEQUENCE seq_testabc START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 100;


CREATE TABLE testabc (
    id         bigint DEFAULT nextval('seq_testabc') NOT NULL,
    val        int                    NOT NULL, -- pole pomocne w testach, tylko do sum kontrolnych
    f1         int                    NOT NULL, -- pole na które nakładane są warunki, normalnie jest to id obce z innej małej tabeli rzędu 12 rekordów
    f2         int                    NOT NULL, -- pole na które nakładane są warunki, normalnie jest to id obce z innej małej tabeli rzędu 12 rekordów
    f3         int                    NOT NULL, -- pole na które nakładane są warunki, normalnie jest to id obce z innej małej tabeli rzędu 1000 rekordów
    st1        character varying(20)  NOT NULL, -- pole które zawiera dane tylko do wyświetlania, nigdy nie nakładamy warunków na to pole
    st2        character varying(20)  NOT NULL, -- pole które zawiera dane tylko do wyświetlania, nigdy nie nakładamy warunków na to pole
    st3        character varying(20)  NOT NULL  -- pole które zawiera dane tylko do wyświetlania, nigdy nie nakładamy warunków na to pole
);


create or replace function mk_data(length integer) returns void as 
$$
declare
  i      integer;
  vval   bigint;
  vf1    integer;
  vf2    integer;
  vf3    integer;
  vst1   text;
  vst2   text;
  vst3   text;
begin
  for i in 1..length loop
    vval = floor( random() * 1000000000 );
    vf1  = floor( random() *   12 + 1 );
    vf2  = floor( random() *   12 + 1 );
    vf3  = floor( random() * 1000 + 1 );
    vst1 = random_string( 20 );
    vst2 = random_string( 20 );
    vst3 = random_string( 20 );        
    execute 'insert into testabc (val,f1,f2,f3,st1,st2,st3) values($1, $2, $3, $4, $5, $6, $7)' using vval, vf1, vf2, vf3, vst1, vst2, vst3;
  end loop;
end;
$$ language plpgsql;


begin;
\timing on
select mk_data(10000000);
\timing off
commit;


SELECT nspname || '.' || relname AS "relation",
    pg_size_pretty(pg_total_relation_size(C.oid)) AS "total_size"
  FROM pg_class C
  LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
  WHERE nspname NOT IN ('pg_catalog', 'information_schema')
    AND C.relkind <> 'i'
    AND nspname !~ '^pg_toast'
  ORDER BY pg_total_relation_size(C.oid) DESC;

--czy wszystko może zmieścić się w ram? ( u mnie 4000MB )
show shared_buffers ;
  

\timing on
-- jaki jest narzut czasowy na najprostsze zapytanie? Nie traci czasu na jakieś logi?
select id from testabc limit 1;  -- tutaj powinien się połączyć ( u mnie czasy od 0.2ms do 0.5ms )


--test bez indeksów
select * from testabc where f1=3 and f2=5 and f3 in ( 1,5,17,20,33,81,121,301,345,400,451,501,502,503,800,922,981) and id <= 1321875 order by id desc limit 30;
explain (analyze, buffers) select * from testabc where f1=3 and f2=5 and f3 in ( 1,5,17,20,33,81,121,301,345,400,451,501,502,503,800,922,981) and id <= 1321875 order by id desc limit 30;


--index na samo id
create index idx1 on testabc (id);
select * from testabc where f1=3 and f2=5 and f3 in ( 1,5,17,20,33,81,121,301,345,400,451,501,502,503,800,922,981) and id <= 2321875 order by id desc limit 30;
explain (analyze, buffers) select * from testabc where f1=3 and f2=5 and f3 in ( 1,5,17,20,33,81,121,301,345,400,451,501,502,503,800,922,981) and id <= 2321875 order by id desc limit 30;


--index na wszystkie pola, id na końcu
create index idx2 on testabc (f1,f2,f3,id);
select * from testabc where f1=3 and f2=5 and f3 in ( 1,5,17,20,33,81,121,301,345,400,451,501,502,503,800,922,981) and id <= 3321875 order by id desc limit 30;
explain (analyze, buffers) select * from testabc where f1=3 and f2=5 and f3 in ( 1,5,17,20,33,81,121,301,345,400,451,501,502,503,800,922,981) and id <= 3321875 order by id desc limit 30;


Wg mnie jednak jesteś sporym ignorantem. Twoim zdaniem ilość danych i schemat bazy nie powinny mieć wpływu, naszym zdaniem powinny.

Tak, mogą mieć wpływ, ale zwróć uwagę, o jakie 'inne dane' chodzi. Inne dane to np. dodatkowe kolumny w tabeli na które nie ma założonego żadnego warunku w zapytaniu. Albo inne dane w tabeli która nie bierze udziału w zapytaniu. Albo inne wartości danych, bo dane pochodzą z funkcji rand. Jeśli ktoś nie zna wpływu takich szczegółów, to po prostu nie jest specjalistą, a ignorancja to raczej oznacza kompletny brak wiedzy z całej dziedziny.

0

Chłopie, weź się ogarnij.
Nie rozumiesz baz. Funkcje masz zj..bane. Twoja random_string jest wolna, bo: plpgsql, a nie sql, zaweira for, za ktorymi bazy nie przepadają.
Moja:

CREATE OR REPLACE FUNCTION random_text(znakow integer)
  RETURNS text AS
$BODY$
  select array_to_string(array_agg(znak),'') from (select generate_series(1,znakow), (array['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','R','S','T','U','V','W','X','Y','Z'])[floor(1+random()*60)] znak)x;
$BODY$
  LANGUAGE sql VOLATILE
  COST 1;

robi to samo, a jest o 30% szybsza.
mk_data, to jest tak skopana, że szkoda gadać.
Czy baza zmieści się w RAM? też pytanie...a powinna? po co? to nie ORACLE, który zabiera wszystko, co ma. Dalej nie chce mi si analizowac, bo pisales o tym pierdyliard razy juz...

0

Troszeczkę prześledziłem temat i mam kilka pytań do autora.
Mianowicie, jak zachowałby się twój system w przypadku BIGDATA bo tu mam duże wątpliwości co do działania w ramie, co z grupowaniem danych i funkcjami agregacyjnymi?
No i dla mnie najważniejszą kwestią, co z transakcjami oraz prawami dostępu?

0

Co taka cisza? :D

0
Deltech napisał(a):

Troszeczkę prześledziłem temat i mam kilka pytań do autora.
Mianowicie, jak zachowałby się twój system w przypadku BIGDATA bo tu mam duże wątpliwości co do działania w ramie, co z grupowaniem danych i funkcjami agregacyjnymi?
No i dla mnie najważniejszą kwestią, co z transakcjami oraz prawami dostępu?

Nie wiem jakiego systemu dotyczy pytanie. Czy chodzi o tę hipotetyczną specjalistyczną bazę danych jaką bym chciał napisać i zastąpic bazę transakcyjną?

Jeśli pytanie dotyczy takiej bazy, to generalnie z tego typu oprogramowaniem są ogromne problemy, problemy pojawiają się nie tylko przy grupowaniu i transakcjach. Większość problemów da się lepiej lub gorzej rozwiązać. Rozwiązania wymagają dużego nakładu pracy programistycznej. Transakcje projektuje się oddzielnie dla każdego systemu, często perfekcyjne wykonanie transakcji jest w ogole niemożliwe, stosuje się jakieś protezy, albo dopuszcza się jakieś minimalne błędy w danych. Wszystko zależy od systemu, ponieważ w każdym systemie inne operacje na bazie są objęte transakcją. Jesli podasz przykład operacji wymagającej transakcji, to możemy zastanowić się nad rozwiązaniem.

3
Duży Terrorysta napisał(a):

Co taka cisza? :D

Autor udowodnił, że bazy SQL są wolne i wszyscy zabrali się za przepisywanie ich na C.

0

@vpiotr autor mowil o C++ to jest kod o ktorym mowa i autor nigdy nawet do niego nie zalinkowal (mimo ze byl proszony wielokrotnie)

// g++ -O3 main.cpp -o if3
// time ./if3
// sum = 96621642
//
// real    0m42.073s
// user    0m41.763s
// sys     0m0.324s
// 41.763s / 1000 zapytań = 41.763ms na jedno zapytanie przy fulscanie 20mln rekordów w ram.
 
#include <cstdio>
#include <cstdlib>
 
 
#define SIZE (20 * 1000000)
 
 
struct IF3 {
    int id;
    int value;
    int pole1;
    int pole2;
    int pole3;
};
 
static void init( IF3 if3[] ) {
    for( int i=0 ; i<SIZE ; i++ ) {
        if3[i].id = i+1;
        if3[i].value = rand() % 1024;
        if3[i].pole1 = rand() % 32;
        if3[i].pole2 = rand() % 32;
        if3[i].pole3 = rand() % 1024;
    }
}
 
static int test1( const IF3 if3[] , const int v1 , const int v2) {
    int sum = 0;
    bool in[1024];
    for( int i=0 ; i<1024 ; i++ )
        in[i] = false;
    in[ 10] = true;
    in[ 20] = true;
    in[ 31] = true;
    in[101] = true;
    in[115] = true;
    in[201] = true;
    in[217] = true;
    in[301] = true;
    in[380] = true;
    in[401] = true;
    for( int i=0 ; i<SIZE ; i++ ) {
        if( in[ if3[i].pole3 ] && if3[i].pole1 == v1 && if3[i].pole2 == v2 )
            sum += if3[i].value;
    }
    return sum;
}
 
 
int main(int argc, char *argv[])
{
    IF3 *const if3 = new IF3[SIZE];
    init( if3 );
    int sum = 0;
    for( int i=0 ; i<1000 ; i++ )
        sum += test1( if3 , (i+5) % 32 , (i*5+4) % 32 );
    delete[] if3;
    printf("sum = %d\n",sum);
    fflush(stdout);
    return 0;
} 

ale tak jak wspominalem to jest bardziej C z klasami a rownie dobrze jak pisze vpiotr to moglbybyc po prostu C

A autor odpowiada wymijajaco na kazde stawiane stwierdzenie (co zapewne takze zrobi do mojego postu lub go zignoruje)

(Po prosilem linka do kodow w innych jezykach ktorych napisal te testy i nigdy ich nie dostalem, za to dostalem wyjasnienie pojecia o ktore nawet nie pytalem)

0
artur_bredzki napisał(a):

Jeśli pytanie dotyczy takiej bazy, to generalnie z tego typu oprogramowaniem są ogromne problemy, problemy pojawiają się nie tylko przy grupowaniu i transakcjach. Większość problemów da się lepiej lub gorzej rozwiązać. Rozwiązania wymagają dużego nakładu pracy programistycznej. Transakcje projektuje się oddzielnie dla każdego systemu, często perfekcyjne wykonanie transakcji jest w ogole niemożliwe, stosuje się jakieś protezy, albo dopuszcza się jakieś minimalne błędy w danych. Wszystko zależy od systemu, ponieważ w każdym systemie inne operacje na bazie są objęte transakcją. Jesli podasz przykład operacji wymagającej transakcji, to możemy zastanowić się nad rozwiązaniem.

Pewnego razu brałem udział w szczególnym spotkaniu projektowym. Kierownik zespołu opisał problem, zrobiliśmy burzę mózgów i przedstawiliśmy swoje propozycje rozwiązań.
Przyszła kolej na takiego starego inżyniera, który podał swoje uniwersalne rozwiązanie: "To jest źle zrobione, i trzeba to tak zrobić, żeby było dobrze."

0
fasadin napisał(a):

ale tak jak wspominalem to jest bardziej C z klasami a rownie dobrze jak pisze vpiotr to moglbybyc po prostu C

Nie jest to C, bo chociażby w C nie ma operatora new.

fasadin napisał(a):

A autor odpowiada wymijajaco na kazde stawiane stwierdzenie (co zapewne takze zrobi do mojego postu lub go zignoruje)

U mnie cały czas na pełnej wersji bazy to zapytanie potrafi trwać kilka sekund. Nie wiem czemu tak się dzieje.

fasadin napisał(a):

(Po prosilem linka do kodow w innych jezykach ktorych napisal te testy i nigdy ich nie dostalem, za to dostalem wyjasnienie pojecia o ktore nawet nie pytalem)

Po co robić coś, co jest oczywiste i można dość dokładnie ocenić wyniki na podstawie wersji w C++?

0
artur_bredzki napisał(a):

Pierwszy szczegół dotyczy tego, na jakim sprzęcie, na ilu komputerach był zainstalowany system.

Ale przecież to jest bardzo ważny feature baz danych, że są skalowalne w bok.

artur_bredzki napisał(a):

Drugi szczegół dotyczy tego, czy system zrównoleglał operacje w sposób inny niż oferuje sama baza danych. Proszę, nie zrozum mnie źle, nawet kiepska baza może umożliwić 1TB insertów w godzinę, jeśli trud zrównoleglenia przeniesie się na projektanta i programistów systemu, a nie na bazę danych. Nie chcę zasugerować że Oracle jest kiepską bazą, tylko po prostu brakuje mi tych szczegółów.

Znaczy... Z jakiegoś powodu obsługa miliona różnych operacji w godzinę jest dla Ciebie czymś mniej karkołomnym niż obsługa jednej kolejki zadań z milionem operacji?

Przecież to są najważniejsze plusy baz danych - dbają o synchronizację (niełatwe zadanie) przy naprawdę dużych wolumenach danych.

Jeśli chodzi o bazę - powiedzmy - z tysiącem rekordów to wydaje mi się, że jakieś 75% średniozaawansowanych programistów jest w stanie napisać coś, co będzie kilkukrotnie szybsze od dowolnej relacyjnej kobyły. Problemy zaczynają się przy dużych objętościach i mnogości zapytań - wtedy łatwo o zagłodzenie wątków, zużycie pamięci itp.

0
artur_bredzki napisał(a)

Po co robić coś, co jest oczywiste i można dość dokładnie ocenić wyniki na podstawie wersji w C++?

Po co wdawać się z tobą w dyskusje, skoro już pierwszy post pozwala dość dokładnie ocenić jej przebieg... #filozoraptor

Skoro zapytanie na tzw. pełnej bazie danych, trwa kilka sekund, to w pierwszej kolejności sprawdza się samo zapytanie. Plan zapytania jest tylko ogólną wskazówką co do tego jak może być ono wykonane. Następnie sprawdza się czy baza nie ma włączonych jakiś dziwnych konfiguracji w rodzaju logowania pewnych zapytań, zapisywania średnich czasów wyszukiwania, czy zapisywania dostępów do rekordów.

I najważniejsze nie porównuje się czasu działania zapytania z czasem testu napisanego ad hoc, ponieważ test wykonuje tylko niewielki ułamek tego co robi silnik bazy danych w czasie procesowania zapytania.

0
wartek01 napisał(a):
artur_bredzki napisał(a):

Pierwszy szczegół dotyczy tego, na jakim sprzęcie, na ilu komputerach był zainstalowany system.

Ale przecież to jest bardzo ważny feature baz danych, że są skalowalne w bok.

Czy nadal mówimy o tej samej klasie baz danych, czy o jakiejś nowej? Hadoop to słyszalem że tak się skaluje, ale przykładowy postgres to chyba tylko do odczytu?

artur_bredzki napisał(a):

Drugi szczegół dotyczy tego, czy system zrównoleglał operacje w sposób inny niż oferuje sama baza danych. Proszę, nie zrozum mnie źle, nawet kiepska baza może umożliwić 1TB insertów w godzinę, jeśli trud zrównoleglenia przeniesie się na projektanta i programistów systemu, a nie na bazę danych. Nie chcę zasugerować że Oracle jest kiepską bazą, tylko po prostu brakuje mi tych szczegółów.

Znaczy... Z jakiegoś powodu obsługa miliona różnych operacji w godzinę jest dla Ciebie czymś mniej karkołomnym niż obsługa jednej kolejki zadań z milionem operacji?
</quote>
Nie, nie o to chodziło.

artur_bredzki napisał(a):

Przecież to są najważniejsze plusy baz danych - dbają o synchronizację (niełatwe zadanie) przy naprawdę dużych wolumenach danych.
Jeśli chodzi o bazę - powiedzmy - z tysiącem rekordów to wydaje mi się, że jakieś 75% średniozaawansowanych programistów jest w stanie napisać coś, co będzie kilkukrotnie szybsze od dowolnej relacyjnej kobyły. Problemy zaczynają się przy dużych objętościach i mnogości zapytań - wtedy łatwo o zagłodzenie wątków, zużycie pamięci itp.

Właśnie dlatego pytałem, na jakim sprzęcie to działało i jak to było zrównoleglane. Czy baza dała radę, czy przerzucono trud na programistów.

1 użytkowników online, w tym zalogowanych: 0, gości: 1